/* 
 * 		Minesweeper Game
 * 
 */

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class Game extends JFrame implements ActionListener ,MouseListener {

	//Fields needed for game.. 
	private int numRows = 10;
	private int numColumns = 10;
	private int numMines = 10;
	boolean lost = false ;
	boolean won = false ;
	GridLayout layout = new GridLayout( numRows , numColumns);
	boolean [][] mines = new boolean [numRows] [numColumns];
	boolean [][] clickable = new boolean [numRows][numColumns];
	boolean [][] clickDone = new boolean [numRows][numColumns];
	Integer [][] numbers = new Integer[numRows][numColumns];
	JButton [][] buttons = new JButton[numRows][numColumns];
	JMenuItem newGameButton = new JMenuItem("New Game");
	JMenuItem difficulty = new JMenuItem("Options");
	JLabel mineLabel = new JLabel("Mines: " + numMines + ", Marked: 0");
	JPanel p = new JPanel();
	
	//Constructor for game... Sets up UI and initlizes everything. 
	public Game(){
		p.setLayout(layout);
		setup();
		for (int x = 0 ; x < numRows ; x++){
			for(int y = 0; y < numColumns; y++){
				p.add(buttons[x][y]);	
			}
		}
		JMenuBar mb = new JMenuBar();
		JMenu m = new JMenu ("File");
		newGameButton.addActionListener(this);
		m.add (newGameButton);
		difficulty.addActionListener(this);
		m.add(difficulty);
		mb.add(m);
		mb.add(m);
		this.setJMenuBar(mb);
		this.add(p);
		this.add(mineLabel,BorderLayout.SOUTH);
		this.pack();
		this.setVisible(true);
	}
	
	//Sets up UI
	public void setup(){
		for ( int x = 0 ; x < numRows ; x++) {
			for ( int y = 0 ; y < numColumns ; y ++ ) {
				mines [x][y] = false;
				clickDone[x][y] = false;
				clickable[x][y] = true;
				buttons [x][y] = new JButton();
				buttons [x][y].setPreferredSize(new Dimension(50,50 ));
				buttons [x][y].addActionListener (this);
				buttons [x][y].addMouseListener (this);
			}
		}
		fillMines();
		fillNumbers();
	}
	
	//Way to reset UI resetting when changing number of columns, rows, and mines
	private void setup1() {
		this.remove(p);
		p = new JPanel();
		layout = new GridLayout(numRows, numColumns);
		p.setLayout(layout);
		buttons = new JButton[numRows][numColumns];
		mines = new boolean[numRows][numColumns];
		clickDone = new boolean[numRows][numColumns];
		clickable = new boolean [numRows][numColumns];
		numbers = new Integer[numRows][numColumns] ;
		setup();
		for (int x = 0 ; x < numRows ; x++){
			for(int y = 0; y < numColumns; y++){
				p.add(buttons[x][y]);	
			}
		}
		this.add(p);
		this.pack();
		fillMines();
		fillNumbers();
		
	}
	
	//Way to reset UI after game ends... 
	private void setup2(){
		for (int x = 0; x < numRows; x++){
			for (int y = 0; y < numColumns; y++){
				mines[x][y] = false ;
				clickDone[x][y] = false;
				clickable[x][y] = true;
				buttons[x][y].setEnabled(true);
				buttons[x][y].setText("");
			}
		}
		fillMines();
		fillNumbers();
		lost = false;
		mineLabel.setText("Mines: " + numMines + ", Marked: 0");
		
	}
	
	//Populates mine on the game.
	public void fillMines ( ) {
		int needed = numMines ;
		while ( needed > 0 ) {
			int x = (int)Math.floor(Math.random()*numRows);
			int y = (int)Math.floor(Math.random()*numColumns);
			if (!mines[x][y]) {
				mines [x][y] = true;
				numbers[x][y] = -1;
				needed-- ;
				
			}
		}
	}
	
	//Populates numbers for the game.
	public void fillNumbers(){
		initlizeNumbers();
		for(int i = 0; i < numRows; i++){
			for(int j = 0; j < numColumns; j++){
				if(mines[i][j] == false){
					numbers[i][j] = returnNumberofSurroundingMines(i,j);
				}
				else{
					numbers[i][j] = -1;
				}
			}
		}
	}
	
	//Function that helps fill numbers. 
	private int returnNumberofSurroundingMines(int i, int j) {
		int numberOfMines = 0;
		if(i-1 >= 0){
			if(mines[i-1][j]){
				numberOfMines += 1;
			}
		}
		if(i+1 < numRows ){
			if(mines[i+1][j]){
				numberOfMines += 1;
			}
		}
		if(j-1 >= 0){
			if(mines[i][j-1]){
				numberOfMines += 1;
			}
		}
		if(j+1 < numColumns){
			if(mines[i][j+1]){
				numberOfMines += 1;
			}
		}
		if((i-1 >= 0) && (j-1 >= 0)){
			if(mines[i-1][j-1]){
				numberOfMines += 1;
			}
		}
		if((i+1 <numRows) && (j+1 < numColumns)){
			if(mines[i+1][j+1]){
				numberOfMines += 1;
			}
		}
		if((i+1 < numRows) && (j-1 >= 0)){
			if(mines[i+1][j-1]){
				numberOfMines += 1;
			}
		}
		if((i-1 >= 0) && (j+1 < numColumns)){
			if(mines[i-1][j+1]){
				numberOfMines += 1;
			}
		}
		
		return numberOfMines;
	}

	//Sets Integer array to 0 since array is not a primitive type. 
	private void initlizeNumbers() {
		for(int i = 0; i < numRows; i++){
			for(int j = 0; j < numColumns; j++){
				numbers[i][j] = 0;
			}
		}
		
	}

	//Automated methods had to override that do nothing... 
	@Override
	public void mouseClicked(MouseEvent arg0) {
		// DOES NOTHING
	}

	@Override
	public void mouseEntered(MouseEvent arg0) {
		// DOES NOTHING
	}

	@Override
	public void mouseExited(MouseEvent arg0) {
		// DOES NOTHING
	}

	//On a right click sets it to an X
	@Override
	public void mousePressed(MouseEvent e) {
		if ( e.getButton ( ) == 3 ) {
			int n = 0 ;
			for (int x = 0; x < numRows; x++){
				for (int y = 0; y < numColumns; y++){
					if ( e.getSource ( ) == buttons[x][y] ) {
						clickable[x][y] = !clickable[x][y] ;
					}
					if (!clickDone[x][y]){
						if (!clickable[x][y]){
							buttons[x][y].setText("X");
							n++;
						} else {
							buttons[x][y].setText("");
						}
						mineLabel.setText ("Mines: " + numMines + ", Marked: " + n);
					}
				}
			}
		}
	}

	//Requried to override does nothing... 
	@Override
	public void mouseReleased(MouseEvent arg0) {
		// DOES NOTHING
	}

	// Used for menu action.. 
	@Override
	public void actionPerformed(ActionEvent e) {
		if ( e.getSource ( ) == difficulty ) {
			numRows = Integer.parseInt((String)JOptionPane.showInputDialog (
					this, "Rows", "Rows", JOptionPane.PLAIN_MESSAGE, null,
					null, 10));
			numColumns = Integer.parseInt((String)JOptionPane.showInputDialog (
					this, "Columns", "Columns", JOptionPane.PLAIN_MESSAGE,
					null, null, 10));
			numMines = Integer.parseInt((String)JOptionPane.showInputDialog ( 
					this, "Mines", "Mines",	JOptionPane.PLAIN_MESSAGE,
					null, null, 10));
			setup1();
		}
		if (!won) {
			for(int x = 0; x < numRows; x++) {
				for (int y = 0; y < numColumns; y++) {
					if ( e.getSource() == buttons [x][y] && !won && clickable[x][y]) {
						doCheck(x, y);
						break ;
					}
				}
			}
		}
		if (e.getSource() == newGameButton){
			setup2();
			won = false;
			return;
 
		}
		checkWin();
	}

	private void checkWin() {
		for (int x = 0; x < numRows; x++){
			for (int y = 0; y < numColumns; y++){
				if (!clickDone[x][y]) {
					if (mines[x][y]){
						continue ;
					} else {
						return ;
					}
				}
			}
		}
		doWin();
	}

	//does the filling out of numbers till it hits non zero numbers.. 
	private void doCheck(int i, int j) {
		// TODO Auto-generated method stub
		clickDone[i][j] = true;
		buttons[i][j].setEnabled(false);
		buttons[i][j].setText(numbers[i][j].toString());
		boolean lost = false;
		
		if (mines[i][j] == true){
			lost = true;
			doLose();
			return;
		}
		
		if(numbers[i][j] != 0){
			return;
		}
		
		if(i-1 >= 0 && !lost){
			if(numbers[i-1][j] == 0){
				clickDone[i-1][j] = true;
				buttons[i-1][j].doClick();
			}
		}
		if(i+1 < numRows  && !lost){
			if(numbers[i+1][j] == 0){
				clickDone[i+1][j] = true;
				buttons[i+1][j].doClick();
			}
		}
		if(j-1 >= 0 && !lost){
			if(numbers[i][j-1] == 0){
				clickDone[i][j-1] = true;
				buttons[i][j-1].doClick();
			}
		}
		if(j+1 < numColumns && !lost){
			if(numbers[i][j+1] == 0){
				clickDone[i][j+1] = true;
				buttons[i][j+1].doClick();
			}
		}
		if((i-1 >= 0) && (j-1 >= 0) && !lost){
			if(numbers[i-1][j-1] == 0){
				clickDone[i-1][j-1] = true;
				buttons[i-1][j-1].doClick();
			}
		}
		if((i+1 <numRows) && (j+1 < numColumns) && !lost){
			if(numbers[i+1][j+1] == 0){
				clickDone[i+1][j+1] = true;
				buttons[i+1][j+1].doClick();
			}
		}
		if((i+1 < numRows) && (j-1 >= 0) && !lost){
			if(numbers[i+1][j-1] == 0){
				clickDone[i+1][j-1] = true;
				buttons[i+1][j-1].doClick();
			}
		}
		if((i-1 >= 0) && (j+1 < numColumns) && !lost){
			if(numbers[i-1][j+1] == 0){
				clickDone[i-1][j+1] = true;
				buttons[i-1][j+1].doClick();
			}
		}
	}
	
	//Displays win message and resets
	public void doWin(){
		if (!lost && !won){
			won = true ;
			JOptionPane.showMessageDialog(null, "You Win!\nStarting a new game", "You Lose", JOptionPane.INFORMATION_MESSAGE);
			newGameButton.doClick();
		}
	}
 
	//Displays lose message and resets.. 
	public void doLose(){
		if (!lost && !won){
			lost = true ;
			for(int x = 0; x < numRows; x++) {
				for (int y = 0; y < numColumns; y++) {
					if (!clickDone[x][y]){
						buttons[x][y].doClick(0);
					}
				}
			}
			JOptionPane.showMessageDialog(null ,"You Lose!\nStarting a new game" , "You Lose" , JOptionPane.ERROR_MESSAGE);
			setup2();
		}
	}

	//Starts GUI and Game.. 
	public static void main(String... Args){
		new Game();
	}
}
